package uk.ac.aber.paws.client.system; 
  
 import java.io.*;
 
 import java.nio.CharBuffer;  

import uk.ac.aber.paws.client.system.ShutdownDetect.MyWinUser;

 import com.sun.jna.ptr.LongByReference; 
 import com.sun.jna.*; 
 import com.sun.jna.win32.*;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;

import com.sun.jna.platform.win32.WinDef.*;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 16th August 2011
 * 
 * NativeCalls.java
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

 public class NativeCalls { 
          
     public interface Kernel32 extends Library, StdCallLibrary {
    	 
    	 Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class); 
    	 Kernel32 SYNC_INSTANCE = (Kernel32) Native.synchronizedLibrary(INSTANCE); 
              
    	 int GetShortPathNameW(WString lpszLongPath, char[] lpdzShortPath, int cchBuffer); 
            
    	 int GetWindowsDirectoryW(char[] lpdzShortPath, int uSize);
            
    	 int GetTickCount();
    	 
    	 boolean GetVolumeInformationW( 
             char[] lpRootPathName, 
             CharBuffer lpVolumeNameBuffer, 
             int nVolumeNameSize, 
             LongByReference lpVolumeSerialNumber, 
             LongByReference lpMaximumComponentLength, 
             LongByReference lpFileSystemFlags, 
             CharBuffer lpFileSystemNameBuffer, 
             int nFileSystemNameSize 
             );       
              
    	 int SetThreadExecutionState(int EXECUTION_STATE); 
                      
             int ES_DISPLAY_REQUIRED = 0x00000002; 
             int ES_SYSTEM_REQUIRED = 0x00000001; 
             int ES_CONTINUOUS = 0x80000000; 
         } 
  
         public long lastDontSleepCall = 0; 
         public long lastGoToSleepCall = 0; 
          
         public void disableGoToSleep() { 
                  
             if (Platform.isWindows()) { 
                 // Disable go to sleep (every 40s) 
            	 if (System.currentTimeMillis() - lastDontSleepCall > 40000) { 
            		 Kernel32.INSTANCE.SetThreadExecutionState(Kernel32.ES_SYSTEM_REQUIRED | Kernel32.ES_CONTINUOUS);
            		 lastDontSleepCall = System.currentTimeMillis(); 
             	 } 
             }
         } 
         
     public void wakeMonitorUp() { 
         
         if (Platform.isWindows()) { 
             // Disable go to sleep (every 40s) 
         
             Kernel32.INSTANCE.SetThreadExecutionState(Kernel32.ES_DISPLAY_REQUIRED);
        
         } 
     }
      
     public interface MyUser32 extends User32 {
    	 
    	 public static final MyUser32 MYINSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.UNICODE_OPTIONS);

    	 public static final int WM_QUERYENDSESSION = 0x11;

 	    public static int GWL_WNDPROC = -4;

 	    interface WindowProc extends StdCallCallback {
 	        LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
 	    }

 	    int SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);

 	    int SetWindowLong(HWND hWnd, int nIndex, WindowProc dwNewLong);
     }
     
     public interface User32 extends StdCallLibrary {
    	 
    	 User32 INSTANCE = (User32)Native.loadLibrary("user32", User32.class);
    	 
		/**
		 * Contains the time of the last input.
		 * @see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputstructures/lastinputinfo.asp
		 */
		public static class LASTINPUTINFO extends Structure {
			public int cbSize = 8;
		
			/// Tick count of when the last input event was received.
			public int dwTime;
		}

		/**
		 * Retrieves the time of the last input event.
		 * @see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/getlastinputinfo.asp
		 * @return time of the last input event, in milliseconds
		 */
		public boolean GetLastInputInfo(LASTINPUTINFO result);
		
		
 	};


 	/**
	 * Get the amount of milliseconds that have elapsed since the last input event
	 * (mouse or keyboard)
	 * @return idle time in milliseconds
	 */
	public static long getIdleTimeMillisWin32() {
		User32.LASTINPUTINFO lastInputInfo = new User32.LASTINPUTINFO();
		
		User32.INSTANCE.GetLastInputInfo(lastInputInfo);
		
		long idleTime = (Kernel32.INSTANCE.GetTickCount()-lastInputInfo.dwTime);
		
		return idleTime;
	}

	/*public static void catchShutdown() {
		
		//HWND hwnd = new HWND();
       // hwnd.setPointer(Native.getComponentPointer(frame));

	
		User32.WindowProc proc = new User32.WindowProc() {
			
			
            public LRESULT callback(HWND wnd, int msg, WPARAM param, LPARAM param2) {
            	System.out.println(msg);
            	if (msg == User32.WM_QUERYENDSESSION) {}
                	System.out.println("shutdown detected");
                
                	File file = new File("c:/shutdown");
            		try {
            			file.createNewFile();
            		} catch (IOException e) {
            			// TODO Auto-generated catch block
            			e.printStackTrace();
            		}
                return new LRESULT(0);
            }
        };

      //  User32 user32 = (User32)Native.loadLibrary("user32", User32.class); //$NON-NLS-1$
  //      User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, proc);
    
	}
	enum State {
		UNKNOWN, ONLINE, IDLE, AWAY
	};
*/
	
	//Create the callback interface 
	public interface MyListener extends StdCallCallback {

	    public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);




	}
	
	public MyListener listener = new MyListener()
	{
	    public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam)
	    {
	        if (uMsg == MyWinUser.WM_DEVICECHANGE)
	        {
	            // TODO Check If my device was attached or detached
	            return new LRESULT(1);
	        }
	        return new LRESULT(0);
	    }
	};
	
	 public static void main(String args[]) {
		 
		 while (true) {
				System.out.println(getIdleTimeMillisWin32());
			 
			 try {
				 Thread.sleep(5000);
			 }
			 catch (Exception ex) {
				 ex.printStackTrace();
			 }
		 }
		 
		 
	 }
	 
     public void reenableGoToSleep() { 
              
	     if (Platform.isWindows()) { 
             if (System.currentTimeMillis() - lastGoToSleepCall > 40000) { 
                     
                 Kernel32.INSTANCE.SetThreadExecutionState(Kernel32.ES_CONTINUOUS); 
                 lastGoToSleepCall = System.currentTimeMillis(); 
             } 
	     } 
     } 
  
     public String getWindowsDirectory() {
    	 
    	 if (Platform.isWindows()) { 
             char test [] = new char [2+256*2]; 
             int r = Kernel32.INSTANCE.GetWindowsDirectoryW(test, 256); 
             if (r > 0) { 
                     return Native.toString(test); 
             } 
    	 } 
         return null; 
     } 
          
     public String getDiskLabel(File f) { 
                 
    	 if (Platform.isWindows()) {
    		 
    		 String driveName;
    		 
    		 try { 
    			 driveName = f.getCanonicalPath().substring(0, 2) + "\\"; 
                              
    			 char[] lpRootPathName_chars = new char[4]; 
                             
    			 for (int i=0; i<3; i++) { 
    				 lpRootPathName_chars[i] = driveName.charAt(i); 
    			 } 
    			 
                 lpRootPathName_chars[3] = '\0'; 
                 int nVolumeNameSize = 256; 
                 CharBuffer lpVolumeNameBuffer_char = CharBuffer.allocate(nVolumeNameSize); 
                 LongByReference lpVolumeSerialNumber = new LongByReference(); 
                 LongByReference lpMaximumComponentLength = new LongByReference(); 
                 LongByReference lpFileSystemFlags = new LongByReference(); 
                 int nFileSystemNameSize = 256; 
                 CharBuffer lpFileSystemNameBuffer_char = CharBuffer.allocate(nFileSystemNameSize); 
  
                 boolean result2 = Kernel32.INSTANCE.GetVolumeInformationW( 
                         lpRootPathName_chars, 
                         lpVolumeNameBuffer_char, 
                         nVolumeNameSize, 
                         lpVolumeSerialNumber, 
                         lpMaximumComponentLength, 
                         lpFileSystemFlags, 
                         lpFileSystemNameBuffer_char, 
                         nFileSystemNameSize); 
                 
                 if (!result2) { 
                	 return null; 
                 } 
                         
                 String diskLabel = charString2String(lpVolumeNameBuffer_char); 
                             
                 return diskLabel; 
             } 
    		 catch(Exception e) { 
                     e.printStackTrace(); 
             } 
    	 } 
    	 return null; 
     } 
         
     private String charString2String(CharBuffer buf) { 
    	 
         char[] chars = buf.array(); 
         
         int i; 
         
         for (i=0; i<chars.length; i++) { 
                 if (chars[i]=='\0') break; 
         }       
         return new String(chars,0,i); 
     } 

 }  